package scales.xml.parser.pull
import javax.xml.stream._
import scales.utils.io.{ProxiedCloseOnNeedReader, ProxiedCloseOnNeedInputStream}
import scales.utils.resources.{CloseOnNeed, IsClosed, Pool}
import scales.xml.impl
import impl.{FromParser, IsFromParser}
import scales.xml.{
ScalesXml,
defaultOptimisation,
defaultPathOptimisation,
Doc,
XmlItem,
Elem, EndElem,
Xml10,
XmlEvent
}
import scales.xml.parser.strategies.{MemoryOptimisationStrategy, PathOptimisationStrategy, OptimisationToken}
import java.io._
sealed trait SourceUser extends CloseOnNeed {
def getReader( xf : XMLInputFactory ) : XMLStreamReader
}
case class CharacterSourceUser(reader : ProxiedCloseOnNeedReader) extends SourceUser {
def getReader( xf : XMLInputFactory ) = xf.createXMLStreamReader(reader)
protected def doClose = reader.closeResource
}
case class ByteSourceUser(stream : ProxiedCloseOnNeedInputStream) extends SourceUser {
def getReader( xf : XMLInputFactory ) = xf.createXMLStreamReader(stream)
protected def doClose = stream.closeResource
}
trait XmlPulls {
def sourceUser( source : org.xml.sax.InputSource ) = {
val cs = source.getCharacterStream()
if (cs eq null)
ByteSourceUser(ProxiedCloseOnNeedInputStream(source.getByteStream()))
else
CharacterSourceUser(ProxiedCloseOnNeedReader(cs))
}
def pullXmlCompletely[RToken <: OptimisationToken]( source : org.xml.sax.InputSource, strategy : PathOptimisationStrategy[RToken] = defaultPathOptimisation, parserFactoryPool : Pool[XMLInputFactory] = impl.DefaultStaxInputFactoryPool, closeAfterUse : Boolean = true) : Doc = {
val pull = pullXml[RToken](source, strategy, parserFactoryPool, closeAfterUse)
Doc(toTree(pull, strategy), pull.prolog, pull.end)
}
def toTree[RToken <: OptimisationToken]( pull : Iterator[PullType], strategy : PathOptimisationStrategy[RToken] = defaultPathOptimisation ) = {
val token = strategy.createToken(Xml10, IsFromParser)
val buf = new impl.TreeProxies()
while( pull.hasNext ){
pull.next match {
case Left( i : XmlItem ) =>
buf.addChild(i)
case Left( e : Elem ) =>
strategy.beginSubTree(buf, e, token)
case Right(endElem) =>
strategy.elementEnd(buf, token)
}
}
buf.tree
}
def pullXmlResource[RToken <: OptimisationToken](source: org.xml.sax.InputSource, optimisationStrategy : MemoryOptimisationStrategy[RToken] = defaultOptimisation, parserFactoryPool: Pool[XMLInputFactory] = impl.DefaultStaxInputFactoryPool) : (CloseOnNeed, XmlPull) = {
val stream = sourceUser(source)
val pf = parserFactoryPool.grab
implicit val weAreInAParser : FromParser = IsFromParser
import ScalesXml.defaultVersion
(stream,
new XmlPull {
type Token = RToken
val strategy = optimisationStrategy
implicit val token = optimisationStrategy.createToken
val parser = stream.getReader(pf)
val resourceCloser = () => { parserFactoryPool.giveBack(pf) }
start
})
}
def pullXml[RToken <: OptimisationToken](source: org.xml.sax.InputSource, optimisationStrategy : MemoryOptimisationStrategy[RToken] = defaultOptimisation, parserFactoryPool: Pool[XMLInputFactory] = impl.DefaultStaxInputFactoryPool, closeAfterUse: Boolean = true) : XmlPull with java.io.Closeable with IsClosed = {
val stream = sourceUser(source)
val pf = parserFactoryPool.grab
implicit val weAreInAParser : FromParser = IsFromParser
import ScalesXml.defaultVersion
new XmlPull with java.io.Closeable with IsClosed {
type Token = RToken
val strategy = optimisationStrategy
val token = optimisationStrategy.createToken
val parser = stream.getReader(pf)
val resourceCloser = () => { parserFactoryPool.giveBack(pf); stream.closeResource }
private[this] var closed = false
def isClosed = closed
override def internalClose { close }
def close {
if (!closed) {
parser.close
resourceCloser()
closed = true
}
}
start
}
}
def pullXmlReader[RToken <: OptimisationToken]( reader : XMLStreamReader, defaultOptimisationStrategy : MemoryOptimisationStrategy[RToken] = defaultOptimisation) : XmlPull = new XmlPull {
type Token = RToken
implicit val eweAreInAParser : FromParser = IsFromParser
import ScalesXml.defaultVersion
val strategy = defaultOptimisationStrategy
val token = defaultOptimisationStrategy.createToken
val parser = reader
val resourceCloser = () => {}
start
}
type PullType = Either[XmlEvent, EndElem]
implicit def toLeft(ev: XmlEvent) = Left(ev)
implicit def toRight(ev: EndElem) = Right(ev)
}